<?php
namespace app\adminjcb\controller;
use Think\Db;
use app\common\controller\AdminBaseController;

/**
 * 数据库备份还原控制器
 */
class DatabaseController extends AdminBaseController {

	//数据库备份
	public function index($type = null) {
		if (empty($type)) {
			$type = 'export';
		}
		$list = array();
		switch ($type) {
			/* 数据还原 */
			case 'import' :
				//列出备份文件列表
				$path = config('backup.db_path');
				if (!is_dir($path)) {
					mkdir($path, 0755, true);
				}
				$path = realpath($path);
				$flag = \FilesystemIterator::KEY_AS_FILENAME;
				$glob = new \FilesystemIterator($path, $flag);
				$list = array();
				foreach ($glob as $name => $file) {
					if (preg_match('/^\d{8,8}-\d{6,6}-\d+\.sql(?:\.gz)?$/', $name)) {
						$name = sscanf($name, '%4s%2s%2s-%2s%2s%2s-%d');

						$date = "{$name[0]}-{$name[1]}-{$name[2]}";
						$time = "{$name[3]}:{$name[4]}:{$name[5]}";
						$part = $name[6];

						if (isset($list["{$date} {$time}"])) {
							$info = $list["{$date} {$time}"];
							$info['part'] = max($info['part'], $part);
							$info['size'] = $info['size'] + $file -> getSize();
						} else {
							$info['part'] = $part;
							$info['size'] = $file -> getSize();
						}
						$extension = strtoupper(pathinfo($file -> getFilename(), PATHINFO_EXTENSION));
						$info['compress'] = ($extension === 'SQL') ? '-' : $extension;
						$info['time'] = strtotime("{$date} {$time}");
						$list["{$date} {$time}"] = $info;
					}
				}
				break;
			/* 数据备份 */
			case 'export' :
				$list = Db::query('SHOW TABLE STATUS FROM ' . config('database.database'));
				$list = array_map('array_change_key_case', $list);
				//过滤非本项目前缀的表
				foreach ($list as $k => $v) {
					if (stripos($v['name'], strtolower(config('database.prefix'))) !== 0) {
						unset($list[$k]);
					}
				}
				break;
			default :
				$this -> error('参数错误！');
		}
		//渲染模板
		$this -> assign('data_list', $list);
		return $this -> fetch($type);
	}

	//数据库还原
	public function import() {
		$path = config('backup.db_path');
		if (!is_dir($path)) {
			mkdir($path, 0755, true);
		}
		$path = realpath($path);
		$flag = \FilesystemIterator::KEY_AS_FILENAME;
		$glob = new \FilesystemIterator($path, $flag);
		$list = array();
		foreach ($glob as $name => $file) {
			if (preg_match('/^\d{8,8}-\d{6,6}-\d+\.sql(?:\.gz)?$/', $name)) {
				$name = sscanf($name, '%4s%2s%2s-%2s%2s%2s-%d');

				$date = "{$name[0]}-{$name[1]}-{$name[2]}";
				$time = "{$name[3]}:{$name[4]}:{$name[5]}";
				$part = $name[6];

				if (isset($list["{$date} {$time}"])) {
					$info = $list["{$date} {$time}"];
					$info['part'] = max($info['part'], $part);
					$info['size'] = $info['size'] + $file -> getSize();
				} else {
					$info['part'] = $part;
					$info['size'] = $file -> getSize();
				}
				$extension = strtoupper(pathinfo($file -> getFilename(), PATHINFO_EXTENSION));
				$info['compress'] = ($extension === 'SQL') ? '-' : $extension;
				$info['time'] = strtotime("{$date} {$time}");

				$list["{$date} {$time}"] = $info;
			}
		}
		//渲染模板
		$this -> assign('data_list', $list);
		return $this -> fetch();
	}

	/**
	 * 优化表
	 * @param  String $tables 表名
	 * @author rainfer <81818832@qq.com>
	 */
	public function optimize($tables = null) {
		if ($tables) {
			if (is_array($tables)) {
				$tables = implode('`,`', $tables);
				$list = Db::query("OPTIMIZE TABLE `{$tables}`");
				if ($list) {
					$this -> success("数据表优化完成！");
				} else {
					$this -> error("数据表优化出错请重试！");
				}
			} else {
				$list = Db::query("OPTIMIZE TABLE `{$tables}`");
				if ($list) {
					$this -> success("数据表'{$tables}'优化完成！");
				} else {
					$this -> error("数据表'{$tables}'优化出错请重试！");
				}
			}
		} else {
			$this -> error("请指定要优化的表！");
		}
	}

	/**
	 * 修复表
	 * @param  String $tables 表名
	 * @author rainfer <81818832@qq.com>
	 */
	public function repair($tables = null) {
		if ($tables) {
			if (is_array($tables)) {
				$tables = implode('`,`', $tables);
				$list = Db::query("REPAIR TABLE `{$tables}`");
				if ($list) {
					$this -> success("数据表修复完成！");
				} else {
					$this -> error("数据表修复出错请重试！");
				}
			} else {
				$list = Db::query("REPAIR TABLE `{$tables}`");
				if ($list) {
					$this -> success("数据表'{$tables}'修复完成！");
				} else {
					$this -> error("数据表'{$tables}'修复出错请重试！");
				}
			}
		} else {
			$this -> error("请指定要修复的表！");
		}
	}

	/**
	 * 备份单表
	 * @param  String $table 不含前缀表名
	 * @author rainfer <81818832@qq.com>
	 */
	public function exportsql($table = null) {
		if ($table) {
			if (stripos($table, config('database.prefix')) == 0) {
				//含前缀的表,去除表前缀
				$table = str_replace(config('database.prefix'), "", $table);
			}
			if (!in_array($table, db_get_tables())) {
				$this -> error("不存在表" . ' ' . $table);
			}
			force_download_content(date('Ymd') . '_' . config('database.prefix') . $table . '.sql', db_get_insert_sqls($table));
		} else {
			$this -> error('未指定需备份的表');
		}
	}

	/**
	 * 删除备份文件
	 * @param  Integer $time 备份时间
	 * @author rainfer <81818832@qq.com>
	 */
	public function del($time = 0) {
		if ($time) {
			$name = date('Ymd-His', $time) . '-*.sql*';
			$path = realpath(config('backup.db_path')) . DS . $name;
			array_map("unlink", glob($path));
			if (count(glob($path))) {
				$this -> error('备份文件删除失败，请检查权限！', url('import'));
			} else {
				$this -> success('备份文件删除成功！', url('import'));
			}
		} else {
			$this -> error('参数错误！', url('import'));
		}
	}

	public function restore($time = 0, $part = null, $start = null) {
		//读取备份配置
		$config = array(
			'path' => realpath(config('backup.db_path')) . DS, 
			'part' => config('backup.db_part'), 
			'compress' => config('backup.db_compress'), 
			'level' => config('backup.db_level'), 
		);
		if (is_numeric($time) && is_null($part) && is_null($start)) {//初始化
			//获取备份文件信息
			$name = date('Ymd-His', $time) . '-*.sql*';
			$path = realpath(config('backup.db_path')) . DS . $name;
			$files = glob($path);
			$list = array();
			foreach ($files as $name) {
				$basename = basename($name);
				$match = sscanf($basename, '%4s%2s%2s-%2s%2s%2s-%d');
				$gz = preg_match('/^\d{8,8}-\d{6,6}-\d+\.sql.gz$/', $basename);
				$list[$match[6]] = array($match[6], $name, $gz);
			}
			ksort($list);
			//检测文件正确性
			$last = end($list);
			if (count($list) === $last[0]) {
				session('backup_list', $list);
				//缓存备份列表
				$this -> restore(0, 1, 0);
			} else {
				$this -> error('备份文件可能已经损坏，请检查！');
			}
		} elseif (is_numeric($part) && is_numeric($start)) {
			$list = session('backup_list');
			$db = new \Database($list[$part], $config);
			$start = $db -> import($start);
			if (false === $start) {
				$this -> error('还原数据出错！');
			} elseif (0 === $start) {//下一卷
				if (isset($list[++$part])) {
					//$data = array('part' => $part, 'start' => 0);
					$this -> restore(0, $part, 0);
				} else {
					session('backup_list', null);
					$this -> success('还原完成！', url('database/import'));
				}
			} else {
				$data = array('part' => $part, 'start' => $start[0]);
				if ($start[1]) {
					$this -> restore(0, $part, $start[0]);
				} else {
					$data['gz'] = 1;
					$this -> restore(0, $part, $start[0]);
				}
			}
		} else {
			$this -> error('参数错误！');
		}
	}

	public function export($tables = null, $id = null, $start = null) {
		if ( request() -> isPost() && !empty($tables) && is_array($tables)) {//初始化
			//读取备份配置
			$config = array(
				'path' => realpath(config('backup.db_path')) . DS, 
				'part' => config('backup.db_part'), 
				'compress' => config('backup.db_compress'), 
				'level' => config('backup.db_level'), 
			);
			//检查是否有正在执行的任务
			$lock = "{$config['path']}backup.lock";
			if (is_file($lock)) {
				$this -> error('检测到有一个备份任务正在执行，请稍后再试！');
			} else {
				//创建锁文件
				file_put_contents($lock, time());
			}
			//检查备份目录是否可写
			is_writeable($config['path']) || $this -> error('备份目录不存在或不可写，请检查后重试！');
			session('backup_config', $config);
			//生成备份文件信息
			$file = array('name' => date('Ymd-His', time()), 'part' => 1, );
			session('backup_file', $file);
			//缓存要备份的表
			session('backup_tables', $tables);
			//创建备份文件
			$Database = new \Database($file, $config);
			if (false !== $Database -> create()) {
				$tab = array('id' => 0, 'start' => 0);
				return json(array('code' => 1, 'tab' => $tab, 'tables' => $tables, 'msg' => '初始化成功！'));
			} else {
				$this -> error('初始化失败，备份文件创建失败！');
			}
		} elseif ( request() -> isGet() && is_numeric($id) && is_numeric($start)) {//备份数据
			$tables = session('backup_tables');
			//备份指定表
			$Database = new \Database(session('backup_file'), session('backup_config'));
			$start = $Database -> backup($tables[$id], $start);
			if (false === $start) {//出错
				$this -> error('备份出错！');
			} elseif (0 === $start) {//下一表
				if (isset($tables[++$id])) {
					$tab = array('id' => $id, 'start' => 0);
					return json(array('code' => 1, 'tab' => $tab, 'msg' => '备份完成！'));
				} else {//备份完成，清空缓存
					unlink(session('backup_config.path') . 'backup.lock');
					session('backup_tables', null);
					session('backup_file', null);
					session('backup_config', null);
					return json(array('code' => 1, 'msg' => '备份完成！'));
				}
			} else {
				$tab = array('id' => $id, 'start' => $start[0]);
				$rate = floor(100 * ($start[0] / $start[1]));
				return json(array('code' => 1, 'tab' => $tab, 'msg' => "正在备份...({$rate}%)"));
			}
		} else {//出错
			$this -> error('参数错误！');
		}
	}

}
